package com.chaos.payment.service.pay.wx;

import com.github.binarywang.wxpay.bean.result.WxPayRefundQueryV3Result;
import com.github.binarywang.wxpay.bean.request.WxPayRefundV3Request.Amount;

import cn.hutool.core.lang.Assert;
import com.github.binarywang.wxpay.bean.payscore.*;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.json.JSONUtil;
import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest;
import com.github.binarywang.wxpay.bean.request.WxPayOrderQueryV3Request;
import com.github.binarywang.wxpay.bean.request.WxPayRefundV3Request;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;
import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryV3Result;
import com.github.binarywang.wxpay.bean.result.WxPayRefundV3Result;
import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result;
import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.PayScoreService;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.service.impl.PayScoreServiceImpl;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
import com.chaos.framework.model.dto.result.data.DataResult;
import com.chaos.framework.model.enums.ResultEnum;
import com.chaos.framework.model.exception.BusinessException;
import com.chaos.order.api.data.TPayWxConfigData;
import com.chaos.order.api.dto.pay.request.PayRequest;
import com.chaos.order.api.dto.pay.request.RefundRequest;
import com.chaos.order.api.dto.pay.response.PayResponse;
import com.chaos.order.api.dto.pay.response.RefundResponse;
import com.chaos.payment.entity.TPayFlow;
import com.chaos.payment.service.pay.AbstractPay;
import com.chaos.payment.service.TPayFlowService;
import com.chaos.payment.service.TPayWxConfigService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import jakarta.servlet.http.HttpServletRequest;
import java.util.Map;

import static com.chaos.order.api.enums.PayErrorCodeEnum.REFUND_ERROR_CODE_2420000;
import static com.chaos.order.api.helper.HttpContextUtils.getHttpServletRequest;

/**
 * @description:
 * @author: crayon
 * @time: 2024/6/17 14:58
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class WxPayApi extends AbstractPay<PayRequest, RefundRequest> {


    private final TPayFlowService tPayFlowService;


    private final TPayWxConfigService tPayWxConfigService;

    /**
     * wx:
     * # 商户号
     * mchId: 1655803045
     * # 小程序 ID
     * appId: wx14f078af8cc2987a
     * payAppId: wx50230defdc5dd516
     * # 商户证书序列号
     * mchCertificateSerialNumber: 73AC2F236F9C2B2E1D0F8ED2EA504EEF9A049DF9
     * # 商户 APIv3 密钥
     * mchAPIv3Key: 2a5c57d377b7cbb8600e6ca998af6953
     * scoreServiceId: 00003004000000170608863471753292
     * scoreIntroduction: 咖啡满电
     * scoreRiskFundName: ADVANCE # 预付款
     * scoreRiskFundAmount: 9900  # 风险金，单位分
     * scoreRiskFundDescription: 预付款可以用来支付您的订单金额，平台不会留存或扣押您的预付款
     *
     * @param request 请求参数
     * @return
     */
    @Override
    public DataResult<PayResponse> pay(PayRequest request) {
        HttpServletRequest httpServletRequest = getHttpServletRequest();
        assert httpServletRequest != null;
        String ip = ServletUtil.getClientIP(httpServletRequest);
        Integer payTradeType = request.getPayTradeType();
        //todo 获取微信配置
        Long merchantId = request.getMerchantId();
        WxPayConfig payConfig = getWxPayConfig(merchantId, false);
        WxPayService wxPayService = new WxPayServiceImpl();
        wxPayService.setConfig(payConfig);
        //是否是 信用分支付
        //boolean creditPayment = OrderEnums.OrderPayTradeTypeEnum.isCreditPayment(payTradeType);
        // todo 三方单号每次要重新生成
        //查询这个订单有没有支付流水
        //savePayFlow(request, payTradeType);

        if (1==1) {
            //信用支付
            return getScorePayResp(request, wxPayService, payConfig);
        } else {
            //常规微信支付
            return getNormalPayResp(request, payTradeType, payConfig, ip, wxPayService);
        }
    }



    private WxPayConfig getWxPayConfig(Long merchantId, boolean refund) {

        TPayWxConfigData byMerchantId = tPayWxConfigService.getByMerchantId(merchantId);
        Assert.notNull(byMerchantId, "商户微信配置不存在");
        WxPayConfig payConfig = new WxPayConfig();
        payConfig.setMchId(byMerchantId.getMchId());
        payConfig.setApiV3Key(byMerchantId.getApiV3Key());
        payConfig.setAppId(byMerchantId.getAppId());
        payConfig.setCertSerialNo(byMerchantId.getCertSerialNo());
        payConfig.setKeyPath(byMerchantId.getKeyPath());
        //v3 需要的配置
        payConfig.setPrivateKeyPath(byMerchantId.getPrivateKeyPath());
        payConfig.setPrivateCertPath(byMerchantId.getPrivateCertPath());
        //微信支付回调地址
        payConfig.setNotifyUrl(refund ? byMerchantId.getRefundNotifyUrl() : byMerchantId.getPayNotifyUrl());
        payConfig.setPayScoreNotifyUrl(byMerchantId.getPayScoreNotifyUrl());
        //payConfig.setUseSandboxEnv(byMerchantId.getSandboxEnv() == 1);
//        WxPayConfig payConfig = new WxPayConfig();
//        payConfig.setMchId("1655803045");
//        payConfig.setApiV3Key("2a5c57d377b7cbb8600e6ca998af6953");
//        payConfig.setAppId("wx50230defdc5dd516");
//        // payConfig.setAppId("wx14f078af8cc2987a");
//        payConfig.setCertSerialNo("73AC2F236F9C2B2E1D0F8ED2EA504EEF9A049DF9");
//        payConfig.setNotifyUrl("http://crayon.free.idcfengye.com//wx/notify/pay");
//        payConfig.setKeyPath("classpath:static/cert/wxpay/apiclient_cert.p12");
//        //v3 需要的配置
//        payConfig.setPrivateKeyPath("classpath:static/cert/wxpay/apiclient_key.pem");
//        payConfig.setPrivateCertPath("classpath:static/cert/wxpay/apiclient_cert.pem");


        return payConfig;
    }

    private DataResult<PayResponse> getNormalPayResp(PayRequest request, Integer payTradeType, WxPayConfig payConfig, String ip, WxPayService wxPayService) {
        WxPayUnifiedOrderV3Result result;
        TradeTypeEnum tradeType = getTradeTypeEnum(payTradeType);
        WxPayUnifiedOrderV3Request wxRequest = buildV3Request(request, payConfig, tradeType, ip);
        try {
            result = wxPayService.unifiedOrderV3(tradeType, wxRequest);
            //result= wxPayService.createOrder(orderRequest);
        } catch (WxPayException e) {
            log.error("wechat_payment_order_creation_failed: request:{} :{}, ", JSONUtil.toJsonStr(wxRequest), tradeType, e);
            return DataResult.error(ResultEnum.INTERNAL_SERVER_ERROR.getCode(), e.getMessage());
        }
        return DataResult.ok(buildV3Response(request, result, tradeType, payConfig));
    }

    private static DataResult<PayResponse> getScorePayResp(PayRequest request, WxPayService wxPayService, WxPayConfig payConfig) {
        PayScoreService payScoreService = new PayScoreServiceImpl(wxPayService);
        Map<String, Object> attachMap = Map.of(
                "userId", request.getUserId(),
                "merchantId", request.getMerchantId()
        );
        WxPayScoreRequest wxPayScoreRequest = buildV3ScoreRequest(request, payConfig, attachMap);
        try {
            WxPayScoreResult serviceOrder = payScoreService.createServiceOrder(wxPayScoreRequest);
            PayResponse payResponse = new PayResponse();
            payResponse.setPayInfo(BeanUtil.beanToMap(serviceOrder));
            return DataResult.ok(payResponse);
        } catch (WxPayException e) {
            log.error("wechat_credit_order_creation_failed: request:{} ", JSONUtil.toJsonStr(wxPayScoreRequest), e);
            return DataResult.error(ResultEnum.INTERNAL_SERVER_ERROR.getCode(), e.getMessage());
        }
    }

    private static WxPayScoreRequest buildV3ScoreRequest(PayRequest request, WxPayConfig payConfig, Map<String, Object> attachMap) {
        WxPayScoreRequest wxPayScoreRequest = new WxPayScoreRequest();
        wxPayScoreRequest.setOutOrderNo(request.getOrderNo());
        wxPayScoreRequest.setAppid(payConfig.getAppId());
        wxPayScoreRequest.setServiceId("00003004000000170608863471753292");
        wxPayScoreRequest.setServiceIntroduction("咖啡满电");
        wxPayScoreRequest.setAttach(JSONUtil.toJsonStr(attachMap));
        wxPayScoreRequest.setNotifyUrl(payConfig.getNotifyUrl());
        wxPayScoreRequest.setOpenid(request.getOpenId());
        wxPayScoreRequest.setNeedUserConfirm(true);
        wxPayScoreRequest.setTotalAmount(BaseWxPayRequest.yuan2Fen(request.getPayAmount()));
        wxPayScoreRequest.setReason("");
        wxPayScoreRequest.setProfitSharing(false);
        // wxPayScoreRequest.setType()
        RiskFund riskFund = new RiskFund();
        riskFund.setName("ADVANCE");
        riskFund.setAmount(BaseWxPayRequest.yuan2Fen(request.getPayAmount()));
        riskFund.setDescription("预付款可以用来支付您的订单金额，平台不会留存或扣押您的预付款");
        wxPayScoreRequest.setRiskFund(riskFund);

        TimeRange timeRange = new TimeRange();
        timeRange.setStartTime("OnAccept");
        wxPayScoreRequest.setTimeRange(timeRange);

        //todo 需要把计价明细传一下
        wxPayScoreRequest.setPostPayments(null);
        return wxPayScoreRequest;
    }

    private static WxPayUnifiedOrderV3Request buildV3Request(PayRequest request, WxPayConfig payConfig, TradeTypeEnum tradeType, String ip) {
        WxPayUnifiedOrderV3Request wxRequest = new WxPayUnifiedOrderV3Request();
        WxPayUnifiedOrderV3Request.Amount amount = new WxPayUnifiedOrderV3Request.Amount();
        amount.setTotal(BaseWxPayRequest.yuan2Fen(request.getPayAmount()));
        amount.setCurrency("CNY");
        wxRequest.setAmount(amount);
        wxRequest.setOutTradeNo(request.getOrderNo());
        //todo 待替换
        wxRequest.setDescription("测试商品");
        wxRequest.setNotifyUrl(request.getNotifyUrl());
        wxRequest.setAppid(payConfig.getAppId());
        //h5支付
        if (tradeType.equals(TradeTypeEnum.H5)) {
            WxPayUnifiedOrderV3Request.SceneInfo sceneInfo = new WxPayUnifiedOrderV3Request.SceneInfo();
            sceneInfo.setPayerClientIp(ip);
            WxPayUnifiedOrderV3Request.H5Info h5Info = new WxPayUnifiedOrderV3Request.H5Info();
            h5Info.setType("Wap");
            sceneInfo.setH5Info(h5Info);
            wxRequest.setSceneInfo(sceneInfo);
        }
        // jsapi
        if (tradeType.equals(TradeTypeEnum.JSAPI)) {
            WxPayUnifiedOrderV3Request.Payer payer = new WxPayUnifiedOrderV3Request.Payer();
            payer.setOpenid("o28qK6zSYQhYHHFpE-OBzhsKVmkA");
            wxRequest.setPayer(payer);
        }
        //附加信息
        Map<String, Object> attachMap = Map.of(
                "userId", request.getUserId(),
                "merchantId", request.getMerchantId()
        );
        wxRequest.setAttach(JSONUtil.toJsonStr(attachMap));
        return wxRequest;
    }

    private static TradeTypeEnum getTradeTypeEnum(Integer payTradeType) {
        TradeTypeEnum tradeType;
        switch (payTradeType) {
            case 1:
                tradeType = TradeTypeEnum.NATIVE;
                break;
            case 2:
                tradeType = TradeTypeEnum.APP;
                break;
            case 3:
                tradeType = TradeTypeEnum.H5;
                break;
            case 4:
            case 5:
                tradeType = TradeTypeEnum.JSAPI;
                break;
            default:
                throw new BusinessException(ResultEnum.INTERNAL_SERVER_ERROR.getCode(), "微信支付场景错误类型错误");
        }
        return tradeType;
    }

    private PayResponse buildV3Response(PayRequest request, WxPayUnifiedOrderV3Result result, TradeTypeEnum tradeType, WxPayConfig payConfig) {
        PayResponse response = new PayResponse();
        String jsonStr = JSONUtil.toJsonStr(result);
        log.info("微信预支付结果：{}", jsonStr);
        Object payInfo = result.getPayInfo(tradeType, payConfig.getAppId(), payConfig.getMchId(), payConfig.getPrivateKey());
        log.info("微信预支付结果 payInfo：{}", payInfo);
        response.setPayInfo(BeanUtil.beanToMap(payInfo));
        response.setThirdRemark("");
        response.setThirdTradeNo(request.getThirdTradeNo());
        response.setPayStatus(1);
        return response;
    }

    @Override
    public DataResult<PayResponse> payQuery(PayRequest request) {
        PayResponse payResponse = new PayResponse();
        //payResponse.setPayStatus(OrderEnums.OrderPayStatusEnum.TO_BE_PAID.getValue());

        // 根据三方支付单号获取商户id
        TPayFlow bytThirdOrderNo = tPayFlowService.getBytThirdOrderNo(request.getThirdOrderNo());
        Assert.notNull(bytThirdOrderNo, "三方支付单号不存在");

        //是否是 信用分支付
        //boolean creditPayment = OrderEnums.OrderPayTradeTypeEnum.isCreditPayment(bytThirdOrderNo.getPayTradeType());
        // todo 获取配置
        WxPayService wxPayService = new WxPayServiceImpl();
        wxPayService.setConfig(getWxPayConfig(request.getMerchantId(), false));
        if (1==1) {
            creditOrderQuery(request, wxPayService, payResponse);
        } else {
            orderQuery(request, wxPayService, payResponse);
        }
        return DataResult.ok(payResponse);
    }

    private void creditOrderQuery(PayRequest request, WxPayService wxPayService, PayResponse payResponse) {
        PayScoreService payScoreService = new PayScoreServiceImpl(wxPayService);
        try {
            WxPayScoreResult wxPayScoreResult = payScoreService.queryServiceOrder(request.getThirdOrderNo(), null);
//            payResponse.setPayStatus(SignPlanServiceOrderStateEnum.DONE.name().equals(wxPayScoreResult.getState())
//                    ? OrderEnums.OrderPayStatusEnum.PAID.getValue() : OrderEnums.OrderPayStatusEnum.TO_BE_PAID.getValue());
            payResponse.setPayInfo(BeanUtil.beanToMap(wxPayScoreResult));
        } catch (WxPayException e) {
            log.error("wechat_credit_order_creation_failed_queryServiceOrder:request:{} ", JSONUtil.toJsonStr(request), e);
        }
    }

    private void orderQuery(PayRequest request, WxPayService wxPayService, PayResponse payResponse) {
        WxPayOrderQueryV3Request wxRequest = new WxPayOrderQueryV3Request();
        wxRequest.setMchid(request.getMchid());
        wxRequest.setTransactionId("");
        wxRequest.setOutTradeNo(request.getThirdOrderNo());
        try {
            WxPayOrderQueryV3Result wxPayOrderQueryV3Result = wxPayService.queryOrderV3(wxRequest);
//            payResponse.setPayStatus("SUCCESS".equals(wxPayOrderQueryV3Result.getTradeState())
//                    ? OrderEnums.OrderPayStatusEnum.PAID.getValue() : OrderEnums.OrderPayStatusEnum.TO_BE_PAID.getValue());
            payResponse.setPayInfo(BeanUtil.beanToMap(wxPayOrderQueryV3Result));
        } catch (WxPayException e) {
            log.error("wechat_credit_order_creation_failed_queryOrderV3:request:{} ", JSONUtil.toJsonStr(request), e);
        }
    }

    @Override
    public DataResult<PayResponse> payCancel(PayRequest request) {
        return null;
    }

    /**
     * 信用分与普通微信支付是同一个退款接口
     * 信用分 setTransactionId && setOutTradeNo
     * 普通 setTransactionId 和 setOutTradeNo 二选一
     *
     * @param request
     * @return
     */
    @Override
    public DataResult<RefundResponse> refund(RefundRequest request) {
        //todo 获取微信配置
        RefundResponse refundResponse = new RefundResponse();
        WxPayConfig payConfig = getWxPayConfig(request.getMerchantId(), true);
        WxPayService wxPayService = new WxPayServiceImpl();
        wxPayService.setConfig(payConfig);
        WxPayRefundV3Request wxRequest = new WxPayRefundV3Request();
        wxRequest.setTransactionId("");
        wxRequest.setOutTradeNo("");
        wxRequest.setOutRefundNo(""); // 商户退款号由商户生成
        wxRequest.setReason(request.getReason());
        wxRequest.setNotifyUrl(payConfig.getNotifyUrl());
        Amount amount = new Amount();
        amount.setRefund(BaseWxPayRequest.yuan2Fen(request.getRefundAmount()));
        amount.setTotal(BaseWxPayRequest.yuan2Fen(request.getRefundTotalAmount()));
        amount.setCurrency("CNY");
        wxRequest.setAmount(amount);
        try {
            WxPayRefundV3Result refundV3Result = wxPayService.refundV3(wxRequest);
            // todo

        } catch (WxPayException e) {
            log.error("wechat_credit_order_creation_failed_refundV3:request:{} ", JSONUtil.toJsonStr(request), e);
          return DataResult.error(REFUND_ERROR_CODE_2420000.getCode(),e.getMessage());
        }
        return DataResult.ok(refundResponse);
    }

    @Override
    public DataResult<RefundResponse> refundQuery(RefundRequest request) {
        WxPayRefundQueryV3Result wxPayRefundQueryV3Result;
        //todo 获取微信配置
        WxPayConfig payConfig = getWxPayConfig(request.getMerchantId(), true);
        WxPayService wxPayService = new WxPayServiceImpl();
        wxPayService.setConfig(payConfig);
        //
        try {
            wxPayRefundQueryV3Result = wxPayService.refundQueryV3(request.getThirdRefundOrderNo());
        } catch (WxPayException e) {
            log.error("wechat_credit_order_creation_failed_refundQueryV3:request:{} ", JSONUtil.toJsonStr(request), e);
        }

        return null;
    }

    @Override
    public Boolean verifySign(String jsonObject, Integer verifyType) {
        return null;
    }
}
